//=======================================================================
//			Copyright (C) Fire64 2013
//=======================================================================

#include "hud.h"
#include "utils.h"
#include "r_local.h"
#include "r_sprite.h"
#include "mathlib.h"
#include "glsl_shaders.h"

bool GLSLShaders::ShaderInit( char *pVertShaderName, char *pFragShaderName )
{
	GLuint pVertShader = LoadShader( pVertShaderName, GL_VERTEX_SHADER_ARB );

	if(!pVertShader)
	{
		return false;
	}

	m_VertShader = pVertShader;

	GLuint pFragShader = LoadShader( pFragShaderName, GL_FRAGMENT_SHADER_ARB );

	if(!pFragShader)
	{
		return false;
	}

	m_FragShader = pFragShader;

	GLuint pShaderProgram = ShaderProgramCreate( pVertShader, pFragShader );

	if(!pShaderProgram)
	{
		return false;
	}

	m_ShaderProgram = pShaderProgram;

	return true;
}

GLuint GLSLShaders::LoadShader( char *pShaderScript, unsigned int shadertype )
{
	GLuint pShader = pglCreateShaderObjectARB( shadertype );

	if( !pShader )
	{
		gEngfuncs.Con_DPrintf( "GLSL Error: Can't create shader: %s\r\n", pShaderScript );
		return NULL;
	}

	char szPatch[MAX_PATH];
	memset( szPatch, 0, sizeof(szPatch) );
	sprintf( szPatch, "scripts/shaders/%s", pShaderScript );

	const char *pShaderBuff = (char*)gEngfuncs.COM_LoadFile( szPatch, 5, NULL );

	if(!pShaderBuff)
	{
		gEngfuncs.Con_DPrintf( "GLSL Error: Can't load shader script: %s\r\n", szPatch );
		return NULL;
	}

	pglShaderSourceARB( pShader, 1, &pShaderBuff, NULL );
	gEngfuncs.COM_FreeFile( (char *)pShaderBuff );

	pglCompileShaderARB( pShader );

	PrintShaderInfo( pShader );

	return pShader;
}

GLuint GLSLShaders::ShaderProgramCreate( GLuint hVertShader, GLuint hFragShader )
{
	GLuint pShaderProgram = pglCreateProgramObjectARB( );

	if( !pShaderProgram )
	{
		gEngfuncs.Con_DPrintf( "GLSL Error: Can't create shader program\r\n" );
		return NULL;
	}

	pglAttachObjectARB( pShaderProgram, hVertShader );
	pglAttachObjectARB( pShaderProgram, hFragShader );

	pglLinkProgramARB( pShaderProgram );

	PrintProgramInfo( pShaderProgram );

	return pShaderProgram;
}

void GLSLShaders::bind( )
{
	pglUseProgramObjectARB( m_ShaderProgram );
}

void GLSLShaders::unbind( )
{
	pglUseProgramObjectARB( 0 );
}

GLint GLSLShaders::GetUniformLocation( char *pShaderVar )
{
	return pglGetUniformLocationARB( m_ShaderProgram, pShaderVar);
}

GLint GLSLShaders::GetAttribLocation( char *pShaderVar )
{
	return pglGetAttribLocationARB( m_ShaderProgram, pShaderVar);
}

void GLSLShaders::PrintShaderInfo(GLuint obj)
{
    int infologLength = 0;
    int charsWritten  = 0;
    char *infoLog;

	pglGetObjectParameterivARB(obj, GL_OBJECT_INFO_LOG_LENGTH_ARB,&infologLength);

    if (infologLength > 0)
    {
        infoLog = (char *)malloc(infologLength);
        pglGetInfoLogARB(obj, infologLength, &charsWritten, infoLog);

		gEngfuncs.Con_DPrintf("%s\n",infoLog);

        free(infoLog);
    }
}

void GLSLShaders::PrintProgramInfo(GLuint obj)
{
    int infologLength = 0;
    int charsWritten  = 0;
    char *infoLog;

	pglGetObjectParameterivARB(obj, GL_OBJECT_INFO_LOG_LENGTH_ARB,&infologLength);

    if (infologLength > 0)
    {
        infoLog = (char *)malloc(infologLength);
        pglGetInfoLogARB(obj, infologLength, &charsWritten, infoLog);

		gEngfuncs.Con_DPrintf("%s\n",infoLog);

        free(infoLog);
    }
}

void GLSLShaders::ShaderUnLoad( )
{
	if (m_VertShader && m_ShaderProgram)
	{
		pglDetachObjectARB(m_ShaderProgram, m_VertShader);
		pglDeleteObjectARB(m_VertShader);
		m_VertShader = NULL;
	}

	if (m_FragShader && m_ShaderProgram)
	{
		pglDetachObjectARB(m_ShaderProgram, m_VertShader);
		pglDeleteObjectARB(m_FragShader);
		m_FragShader = NULL;
	}

	if(m_ShaderProgram)
	{
		pglDeleteObjectARB(m_ShaderProgram);
		m_ShaderProgram = NULL;
	}
}

GLuint GLSLShaders::GetVertShader( )
{
	return m_VertShader;
}

GLuint GLSLShaders::GetFragShader( )
{
	return m_FragShader;
}

GLuint GLSLShaders::GetShaderProgram( )
{
	return m_ShaderProgram;
}
